package c2.cross.business.arena;

import c2.advanced.business.base.KvData;
import c2.cross.business.CrossBody;
import c2.cross.business.arena.data.ArenaMainKV;
import c2.cross.business.arena.data.ArenaRoleFightForm;
import c2.cross.business.base.CrossModule;
import cate.common.game.mail.MailCmd;
import cate.common.table.d.GD;
import cate.common.table.d.GDObj;
import cate.common.table.d.GDOperation;
import cate.common.table.pvp.arena.opponent_range.ArenaOpponentRangeRow;
import cate.common.table.pvp.arena.rank_reward.ArenaRankRewardRow;
import cate.common.util.TimeTool;
import cate.game.admin.cross.CrossAdminCMDs;
import cate.game.pvp.arena.ArenaConstants;
import cate.game.pvp.arena.core.ArenaRoleDetail;
import cate.game.pvp.arena.core.ArenaRoleSnapshot;
import cate.game.pvp.arena.msg.*;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.role.pvp.arena.msg.ArenaFightResultResp;
import cate.game.role.pvp.arena.msg.ArenaViewRankResp;
import cate.game.role.pvp.arena.msg.ArenaViewTopResp;
import com.google.common.collect.Lists;
import cp.solution.business.game.component.NoticeParam;
import cp.solution.rpc.RpcMsgHead;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Slf4j
@NoteClass("跨服擂台赛")
public class ArenaModule extends CrossModule<ArenaMainKV> {
    @NoteField("排行同步锁")
    private final ReentrantLock lock = new ReentrantLock();
    @NoteField("是否有变更")
    private final AtomicBoolean changed = new AtomicBoolean();
    @NoteField("缓存的玩家阵容")
    private final Map<String, ArenaRoleFightForm> cachedFightForms = new ConcurrentHashMap<>();
    @NoteField("排行列表")
    private List<ArenaRoleSnapshot> rankList = new ArrayList<>();
    @NoteField("大神榜")
    private List<ArenaRoleSnapshot> topList = new ArrayList<>();

    public ArenaModule(CrossBody context) {
        super(context);
    }

    @Override
    protected String getStatusKey() {
        return KvData.ARENA;
    }

    @Override
    protected Class<ArenaMainKV> getStatusClass() {
        return ArenaMainKV.class;
    }

    @Override
    public String getName() {
        return "跨服擂台赛";
    }

    @Override
    public void initDependent() throws Exception {
        super.initDependent();
        // 加载常量
        ArenaConstants.loadFromTable(table().common);
        // RPC
        rpc().recv().onFireAndForgetTemplate(ArenaInnerMsg.SYNC_ROLE_FORM, AISyncRoleForm.class, this::syncRoleForm);
        rpc().recv().onRequestResponseTemplate(ArenaInnerMsg.TRY_GET_DETAIL, String.class, this::getRoleDetail);
        rpc().recv().onRequestResponseTemplate(ArenaInnerMsg.RELOAD_ROLE_RANK, String.class, this::reloadRoleRank);
        rpc().recv().onRequestResponseTemplate(ArenaInnerMsg.REFRESH_OPPONENT, String.class, this::refreshOpponents);
        rpc().recv().onRequestResponseTemplate(ArenaInnerMsg.FIGHT_REPORT, AIReportFightResult.class, this::reportFightResult);
        rpc().recv().onRequestResponseTemplate(ArenaInnerMsg.GET_RANK_LIST, String.class, this::getRankList);
        rpc().recv().onRequestResponseTemplate(ArenaInnerMsg.GET_TOP_LIST, Void.class, this::getTopList);
        rpc().recv().onRequestResponseTemplate(ArenaInnerMsg.LIKE_TOP, String.class, this::likeTop);
        // GM
        admin().functionRegister(CrossAdminCMDs.ARENA_RESET_SEASON, params -> {
            log.info("GM重置赛季");
            resetSeason();
            return "重置成功!";
        });
        admin().functionRegister(CrossAdminCMDs.ARENA_CALC_RESET, params -> {
            log.info("GM重算结算时间");
            long nowTime = System.currentTimeMillis();
            status.nextSettleTime = TimeTool.getZeroMondayTimeStamp(nowTime) + TimeTool.TimeCons.WEEK - TimeTool.TimeCons.HOUR;
            if (status.nextSettleTime < nowTime) {
                status.nextSettleTime += TimeTool.TimeCons.WEEK;
            }
            log.info("下个赛季结算时间={}", status.nextSettleTime);
            return "重算成功!";
        });
        // 启动的时候重新加载一下机器人
        reloadRobot();
        // 检查下战力
        status.infoMap.values().forEach(ars -> {
            if (!ars.snapshot.unreal && ars.power != ars.snapshot.base.power) {
                ars.power = ars.snapshot.base.power;
            }
        });
        // 初始化时走一下循环 初始化也在里面
        onInterval();
    }

    @Override
    protected int getIntervalSeconds() {
        return 1;
    }

    @Override
    protected void onInterval() throws Exception {
        super.onInterval();
        long nowTime = System.currentTimeMillis();
        // 检测结算
        if (nowTime >= status.nextSettleTime) {
            log.info("赛季结算-时间={}", status.nextSettleTime);
            resetSeason();
            // 当前为周日23点结算,所以为周日24点前推1小时
            status.nextSettleTime = TimeTool.getZeroMondayTimeStamp(nowTime) + TimeTool.TimeCons.WEEK - TimeTool.TimeCons.HOUR;
            if (status.nextSettleTime < nowTime) {
                status.nextSettleTime += TimeTool.TimeCons.WEEK;
            }
            log.info("下个赛季结算时间={}", status.nextSettleTime);
        }
        // 检测缓存战斗阵容释放
        cachedFightForms.entrySet().removeIf(e -> e.getValue().timeoutTime >= nowTime);
    }

    /**
     * 重新加载机器人
     */
    private void reloadRobot() {
        lock.lock();
        try {
            status.infoMap.entrySet()
                    .removeIf(e -> e.getValue().snapshot.unreal && table().arena.robot_storage.get(e.getValue().robotId) == null);
            table().arena.robot_storage.getList().forEach(row -> {
                if (!status.infoMap.containsKey(row.uid())) {
                    ArenaRoleSnapshot snapshot = new ArenaRoleSnapshot();
                    snapshot.snapshot = context.play.sideBuilder.buildSnapShotByCode(row.insCode1);
                    snapshot.snapshot.uid = row.uid();
                    snapshot.power = row.totalPower;
                    snapshot.score = row.initScore;
                    snapshot.active = true;
                    snapshot.robotId = row.id;
                    status.infoMap.put(row.uid(), snapshot);
                }
            });
            // 标记改变
            changed.set(true);
            checkRank();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 赛季重置
     */
    private void resetSeason() {
        lock.lock();
        try {
            // 结算上个赛季奖励
            checkRank();
            sendRankReward();
            // 排行榜重刷
            rankList.clear();
            topList.clear();
            // 玩家信息更新
            status.infoMap.values().stream().filter(snapshot -> !snapshot.snapshot.unreal)
                    .forEach(snapshot -> {
                        snapshot.rank = 0;
                        snapshot.active = false;
                        snapshot.score = Math.max(snapshot.score * ArenaConstants.SEASON_DISCOUNT / 10000,
                                ArenaConstants.INIT_SCORE);
                    });
            // 机器人信息更新
            table().arena.robot_storage.getList().forEach(row -> {
                if (status.infoMap.containsKey(row.uid())) {
                    ArenaRoleSnapshot snapshot = status.infoMap.get(row.uid());
                    snapshot.snapshot = context.play.sideBuilder.buildSnapShotByCode(row.insCode1);
                    snapshot.snapshot.uid = row.uid();
                    snapshot.power = row.totalPower;
                    snapshot.score = row.initScore;
                    snapshot.active = true;
                } else {
                    ArenaRoleSnapshot snapshot = new ArenaRoleSnapshot();
                    snapshot.snapshot = context.play.sideBuilder.buildSnapShotByCode(row.insCode1);
                    snapshot.snapshot.uid = row.uid();
                    snapshot.power = row.totalPower;
                    snapshot.score = row.initScore;
                    snapshot.active = true;
                    snapshot.robotId = row.id;
                    status.infoMap.put(row.uid(), snapshot);
                }
            });
            // 标记改变
            changed.set(true);
            checkRank();
        } finally {
            lock.unlock();
        }
        try {
            rpc().send().fireAndForgetGameInCrossTemplate(ArenaInnerMsg.SEASON_CHANGE, context().getCode(), null);
        } catch (Exception e) {
            log.error("", e);
        }
    }

    /**
     * 检测重排
     */
    private void checkRank() {
        if (changed.get()) {
            lock.lock();
            try {
                rankList = status.infoMap.values().stream()
                        .filter(snapshot -> snapshot.active)
                        .sorted(ArenaRoleSnapshot.COMPARATOR)
                        .collect(Collectors.toList());
                topList = new ArrayList<>(3);
                for (int i = 0; i < rankList.size(); i++) {
                    ArenaRoleSnapshot snapshot = rankList.get(i);
                    snapshot.rank = i + 1;
                    if (snapshot.rank <= 3) {
                        if (!snapshot.snapshot.unreal) {
                            topList.add(snapshot);
                        } else {
                            topList.add(null);
                        }
                    }
                }
                changed.set(false);
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * 发送赛季排行奖励
     */
    private void sendRankReward() {
        for (ArenaRoleSnapshot ars : rankList) {
            if (ars.snapshot.unreal) {
                continue;
            }
            ArenaRankRewardRow row = table().arena.rank_reward.getRowByRank(ars.rank);
            if (row == null) {
                continue;
            }
            log.info("[Arena] {}(uid:{}) 本期排行为{})", ars.snapshot.base.name,
                    ars.snapshot.uid, ars.rank);
            int starMax = Optional.ofNullable(help().getSnapshot(ars.snapshot.uid))
                    .map(snapshot -> snapshot.shareStarMax)
                    .orElse(0);
            int extra = table().pvpRankPointExtra.base.getExtraParam(starMax);
            MailCmd mail;
            if (extra > 0) {
                MixRes res = new MixRes(row.rewardStr);
                long count = res.count(GDObj.Type.NUM, 13);
                long addNum = count * extra / GD.W_10000;
                MixResItem item = new MixResItem(GDObj.Type.NUM, 13, addNum);
                res.addItem(item);
                mail = new MailCmd()
                        .withContent(ArenaConstants.RANK_MAIL_ID_EX,
                                Lists.newArrayList(
                                        new NoticeParam(ars.rank),
                                        new NoticeParam(starMax),
                                        new NoticeParam((extra / 100.0) + "%"),
                                        new NoticeParam(NoticeParam.NoticeType.MIX_RES, new MixRes().addItem(item))))
                        .withReward(res.toItemsStr());
            } else {
                mail = new MailCmd()
                        .withContent(ArenaConstants.RANK_MAIL_ID,
                                Lists.newArrayList(
                                        new NoticeParam(ars.rank)
                                ))
                        .withReward(row.rewardStr);
            }
            mail.addRole(ars.snapshot.uid)
                    .withSrvId(ars.snapshot.base.srvId)
                    .withOpr(GDOperation.ARENA_RANK_REWARD, ars.rank);
            context.common.sendMail(mail);
        }
    }

    /**
     * 同步玩家阵容
     *
     * @param head 头信息
     * @param msg  消息
     */
    private void syncRoleForm(RpcMsgHead head, AISyncRoleForm msg) {
        // 更新快照
        String uid = msg.snapshot.uid;
        ArenaRoleSnapshot ars = status.infoMap.computeIfAbsent(uid, (k) -> {
            ArenaRoleSnapshot ss = new ArenaRoleSnapshot();
            ss.score = ArenaConstants.INIT_SCORE;
            return ss;
        });
        ars.snapshot = msg.snapshot;
        ars.power = msg.snapshot.base.power;
        // 更新阵容
        ArenaRoleFightForm fightForm;
        if (cachedFightForms.containsKey(uid)) {
            fightForm = cachedFightForms.get(uid);
        } else {
            fightForm = new ArenaRoleFightForm();
            fightForm.uid = uid;
        }
        fightForm.defendVisions = msg.defendVisions;
        fightForm.playSides = msg.playSides;
        db().arena_fight_form.save(fightForm);
    }

    /**
     * 获取玩家详细信息
     *
     * @param head 头信息
     * @param uid  唯一ID
     * @return 详细信息
     */
    public ArenaRoleDetail getRoleDetail(RpcMsgHead head, String uid) {
        if (!status.infoMap.containsKey(uid)) {
            return null;
        }
        ArenaRoleDetail detail = new ArenaRoleDetail();
        detail.base = status.infoMap.get(uid);
        if (detail.base.snapshot.unreal) {
            detail.robotId = detail.base.robotId;
            detail.defendVisions = ArenaConstants.BUILD_DEFAULT_VISION(detail.base.rank);
        } else {
            ArenaRoleFightForm fightForm = tryGetFightForm(uid);
            if (fightForm == null) {
                return null;
            }
            detail.defendVisions = fightForm.defendVisions;
            detail.playSides = fightForm.playSides;
        }
        return detail;
    }

    /**
     * 尝试获取玩家战斗阵容
     *
     * @param uid 唯一ID
     * @return 战斗阵容
     */
    private ArenaRoleFightForm tryGetFightForm(String uid) {
        if (cachedFightForms.containsKey(uid)) {
            return cachedFightForms.get(uid);
        }
        ArenaRoleFightForm fightForm = db().arena_fight_form.get(uid);
        if (fightForm == null) {
            return null;
        }
        fightForm.timeoutTime = System.currentTimeMillis() + 30 * TimeTool.TimeCons.SECOND;
        cachedFightForms.put(uid, fightForm);
        return fightForm;
    }

    /**
     * 重新加载玩家排行和积分
     *
     * @param head 头信息
     * @param uid  唯一ID
     */
    private AIReloadRankAndScore reloadRoleRank(RpcMsgHead head, String uid) {
        // 加载前先检查重排
        checkRank();
        ArenaRoleSnapshot ars = status.infoMap.get(uid);
        if (ars == null) {
            return new AIReloadRankAndScore(uid, 0, ArenaConstants.INIT_SCORE);
        }
        return new AIReloadRankAndScore(uid, ars.rank, ars.score);
    }

    /**
     * 刷新对手列表
     *
     * @param head 头信息
     * @param uid  唯一ID
     */
    private AIRefreshOpponents refreshOpponents(RpcMsgHead head, String uid) {
        // 刷新前先重排
        checkRank();
        List<ArenaRoleSnapshot> temp = rankList;
        int rank = 0;
        boolean robot = false;
        ArenaRoleSnapshot ars = status.infoMap.get(uid);
        // 首次挑战必未机器人
        if (ars == null || !ars.active) {
            robot = true;
        }
        // 未进榜的按榜尾排名处理
        if (ars != null) {
            rank = ars.rank;
        }
        if (rank == 0) {
            rank = temp.get(temp.size() - 1).rank;
        }
        ArenaOpponentRangeRow rangeRow = table().arena.opponent_range.getByRank(rank);
        if (rangeRow == null) {
            return null;
        }
        int start = Math.max(rank + rangeRow.forwardRange, 1);
        int end = Math.min(rank + rangeRow.backwardRange, temp.size());
        // 万一不够，就扩容
        if (end - start < 3) {
            start = 1;
            end = temp.size();
        }
        List<ArenaRoleSnapshot> snapshots = temp.subList(start - 1, end).stream()
                .filter(ss -> !uid.equals(ss.snapshot.uid))
                .collect(Collectors.toList());
        if (robot) {
            snapshots = snapshots.stream().filter(ars_t -> ars_t.snapshot.unreal).collect(Collectors.toList());
            if (snapshots.size() < 3) {
                snapshots = temp.stream().filter(ars_t -> ars_t.snapshot.unreal).collect(Collectors.toList());
            }
        }
        List<ArenaRoleSnapshot> sortList = new ArrayList<>(snapshots);
        // 万一还不够，有多少给多少拉倒
        if (sortList.size() > 3) {
            Collections.shuffle(sortList);
            sortList = new ArrayList<>(sortList.subList(0, 3));
            sortList.sort(ArenaRoleSnapshot.COMPARATOR);
        }
        return new AIRefreshOpponents(sortList);
    }

    /**
     * 上报战斗结果
     *
     * @param head 头信息
     * @param msg  消息
     */
    private ArenaFightResultResp reportFightResult(RpcMsgHead head, AIReportFightResult msg) {
        ArenaRoleSnapshot self = status.infoMap.get(msg.selfUid);
        ArenaRoleSnapshot oppo = status.infoMap.get(msg.oppoUid);
        if (self == null || oppo == null) {
            return null;
        }
        if (!self.active) {
            self.active = true;
            changed.set(true);
        }
        ArenaFightResultResp resp = new ArenaFightResultResp();
        resp.win = msg.win;
        resp.opponent = oppo.snapshot.copy();
        if (msg.win) {
            int selfScore = self.score;
            int oppoScore = oppo.score;
            int winScore = ArenaConstants.BASE_SCORE_CHANGE;
            if (oppoScore > selfScore) {
                winScore += (oppoScore - selfScore) * ArenaConstants.EXTRA_SCORE_CHANGE_RATE / 10000;
            }
            int loseScore = -ArenaConstants.BASE_SCORE_CHANGE;
            self.lock.lock();
            try {
                self.score += winScore;
            } finally {
                self.lock.unlock();
            }
            oppo.lock.lock();
            try {
                int old = oppo.score;
                oppo.score += loseScore;
                oppo.score = Math.max(ArenaConstants.INIT_SCORE, oppo.score);
                loseScore = oppo.score - old;
            } finally {
                oppo.lock.unlock();
            }
            resp.selfScoreChange = winScore;
            resp.oppoScoreChange = loseScore;
            changed.set(true);
        }
        resp.selfScore = self.score;
        resp.oppoScore = oppo.score;
        return resp;
    }

    /**
     * 获取排行榜 只给看前100
     *
     * @param head 头信息
     * @param uid  角色UID
     */
    private ArenaViewRankResp getRankList(RpcMsgHead head, String uid) {
        ArenaViewRankResp resp = new ArenaViewRankResp();
        List<ArenaRoleSnapshot> temp = rankList;
        resp.rankList = temp.size() > 100 ? new ArrayList<>(temp.subList(0, 100)) : new ArrayList<>(temp);
        resp.self = status.infoMap.get(uid);
        return resp;
    }

    /**
     * 获取大神榜
     *
     * @param head 头信息
     * @param v    请求消息空空如也
     */
    private ArenaViewTopResp getTopList(RpcMsgHead head, Void v) {
        return new ArenaViewTopResp(topList);
    }

    /**
     * 点赞大神
     *
     * @param head 头信息
     * @param uid  大神UID
     * @return 是否成功
     */
    private boolean likeTop(RpcMsgHead head, String uid) {
        ArenaRoleSnapshot ars = status.infoMap.get(uid);
        if (ars == null || ars.snapshot.unreal) {
            return false;
        }
        ars.lock.lock();
        try {
            ars.likeCount++;
        } finally {
            ars.lock.unlock();
        }
        return true;
    }
}
